package ee.metadataUtils.impl;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


import ee.metadataUtils.PropertyMetadataAdapterFactoryRegistry;

public class PropertyDescriptorPropertyMetadata extends AbstractPropertyMetadata {

	private PropertyDescriptor _propertyDescriptor;
	private String _name;
	private Type _genericType;

	private Method _readMethod;
	private Method _writeMethod;
	private Class<?> _type;
	private Annotation[] _annotations;
	
	public PropertyDescriptorPropertyMetadata(PropertyMetadataAdapterFactoryRegistry adapterFactoryRegistry, PropertyDescriptor propertyDescriptor) {
		super(adapterFactoryRegistry);
		
		_propertyDescriptor = propertyDescriptor;
		_readMethod = propertyDescriptor.getReadMethod();
		_writeMethod = propertyDescriptor.getWriteMethod();
	}

	@Override
	public String getName() {
		if (_name == null)
		{
			_name = _propertyDescriptor.getName();
		}
		
		return _name;
	}

	@Override
	public Type getGenericType() {
		
		if (_genericType == null)
		{
			_genericType = _readMethod.getGenericReturnType();
		}

		return _genericType;
	}

	@Override
	public Class<?> getType() {
		
		if (_type == null)
		{
			_type = _readMethod.getReturnType();
		}
		
		return _type;
	}

	@Override
	public Object getValue(Object instance) throws Throwable {
		return _readMethod.invoke(instance);
	}

	@Override
	public void setValue(Object instance, Object value) throws Throwable {
		_writeMethod.invoke(instance, value);
	}

	@Override
	public Annotation[] getAnnotations() {
		
		if (_annotations == null)
		{
			List<Annotation> annotations = new ArrayList<Annotation>();
			
			//gather all annotations from field, getter and setter
			
			Field declaredField = null;
			try {
				declaredField = _readMethod.getDeclaringClass().getDeclaredField(getName());
			} catch (SecurityException e) {
			} catch (NoSuchFieldException e) {
			}
			
			if (declaredField != null)
			{
				annotations.addAll(Arrays.asList(declaredField.getAnnotations()));
			}
			
			annotations.addAll(Arrays.asList(_readMethod.getAnnotations()));
			if (_writeMethod != null)
			{
				annotations.addAll(Arrays.asList(_writeMethod.getAnnotations()));
			}
			
			_annotations = annotations.toArray(new Annotation[annotations.size()]);
		}

		return _annotations;
	}

	
}
